home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectSound / CaptureSound / capturesound.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  32.8 KB  |  1,002 lines

  1. //----------------------------------------------------------------------------
  2. // File: CaptureSound.cpp
  3. //
  4. // Desc: The CaptureSound sample shows how to use DirectSoundCapture to capture 
  5. //       sound into a wave file 
  6. //
  7. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #include <windows.h>
  11. #include <basetsd.h>
  12. #include <commdlg.h>
  13. #include <mmreg.h>
  14. #include <dxerr8.h>
  15. #include <dsound.h>
  16. #include "resource.h"
  17. #include "DSUtil.h"
  18.  
  19.  
  20.  
  21.  
  22. //-----------------------------------------------------------------------------
  23. // Function-prototypes
  24. //-----------------------------------------------------------------------------
  25. HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid );
  26. HRESULT FreeDirectSound();
  27.  
  28. INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName,
  29.                                      VOID* pContext );
  30. INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  31. INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  32. HRESULT OnInitDevicesDialog( HWND hDlg );
  33. HRESULT OnInitFormatsDialog( HWND hDlg );
  34. HRESULT ScanAvailableInputFormats();
  35. VOID    GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx );
  36. HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported );
  37. VOID    ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName );
  38. HRESULT OnInputFormatBoxSelected( HWND hDlg );
  39. HRESULT OnFormatsOK( HWND hDlg );
  40.  
  41. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  42. HRESULT OnInitMainDialog( HWND hDlg );
  43. VOID    OnSaveSoundFile( HWND hDlg );
  44. HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput );
  45. HRESULT InitNotifications();
  46. HRESULT StartOrStopRecord( BOOL bStartRecording );
  47. HRESULT RecordCapturedData();
  48.  
  49.  
  50.  
  51.  
  52. //-----------------------------------------------------------------------------
  53. // Defines, constants, and global variables
  54. //-----------------------------------------------------------------------------
  55. #define NUM_REC_NOTIFICATIONS  16
  56. #define MAX(a,b)        ( (a) > (b) ? (a) : (b) )
  57.  
  58. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  59. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  60.  
  61. LPDIRECTSOUNDCAPTURE       g_pDSCapture         = NULL;
  62. LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture        = NULL;
  63. LPDIRECTSOUNDNOTIFY        g_pDSNotify          = NULL;
  64. HINSTANCE                  g_hInst              = NULL;
  65. GUID                       g_guidCaptureDevice  = GUID_NULL;
  66. BOOL                       g_bRecording;
  67. WAVEFORMATEX               g_wfxInput;
  68. DSBPOSITIONNOTIFY          g_aPosNotify[ NUM_REC_NOTIFICATIONS + 1 ];  
  69. HANDLE                     g_hNotificationEvent; 
  70. BOOL                       g_abInputFormatSupported[16];
  71. DWORD                      g_dwCaptureBufferSize;
  72. DWORD                      g_dwNextCaptureOffset;
  73. DWORD                      g_dwNotifySize;
  74. CWaveFile*                  g_pWaveFile;
  75.  
  76.  
  77.  
  78.  
  79. //-----------------------------------------------------------------------------
  80. // Name: WinMain()
  81. // Desc: Entry point for the application.  Since we use a simple dialog for 
  82. //       user interaction we don't need to pump messages.
  83. //-----------------------------------------------------------------------------
  84. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, 
  85.                       INT nCmdShow )
  86. {
  87.     HRESULT hr;
  88.     DWORD dwResult;
  89.     MSG   msg;
  90.     BOOL  bDone;
  91.     HWND  hDlg;
  92.  
  93.     g_hInst = hInst;
  94.     g_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  95.  
  96.     // Create the main dialog box, but keep it hidden for now
  97.     hDlg = CreateDialog( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc );
  98.  
  99.  
  100.     // Display the formats dialog box, and show it
  101.     dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_DEVICES), 
  102.                                  NULL, DevicesDlgProc );
  103.     if( dwResult != IDOK )
  104.     {
  105.         // The user canceled, so stop message pump, 
  106.         // and fall through to the cleanup code
  107.         PostQuitMessage( 0 );
  108.     }
  109.     else
  110.     {
  111.         // Init DirectSound
  112.         if( FAILED( hr = InitDirectSound( hDlg, &g_guidCaptureDevice ) ) )
  113.         {
  114.             DXTRACE_ERR( TEXT("InitDirectSound"), hr );
  115.             MessageBox( hDlg, "Error initializing DirectSound.  Sample will now exit.", 
  116.                         "DirectSound Sample", MB_OK | MB_ICONERROR );
  117.             PostQuitMessage( 0 );
  118.         }
  119.     }
  120.  
  121.  
  122.     // Display the formats dialog box, and show it
  123.     if( dwResult == IDOK )
  124.     {
  125.         dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_FORMATS), 
  126.                                      NULL, FormatsDlgProc );
  127.     }
  128.  
  129.     if( dwResult != IDOK )
  130.     {
  131.         // The user canceled, so stop message pump, 
  132.         // and fall through to the cleanup code
  133.         PostQuitMessage( 0 );
  134.     }
  135.     else
  136.     {
  137.         WAVEFORMATEX wfxInput;
  138.         TCHAR        strInputFormat[255];
  139.         HWND         hInputFormatText;
  140.  
  141.         hInputFormatText = GetDlgItem( hDlg, IDC_MAIN_INPUTFORMAT_TEXT );
  142.  
  143.         ZeroMemory( &wfxInput, sizeof(wfxInput));
  144.         g_pDSBCapture->GetFormat( &wfxInput, sizeof(wfxInput), NULL );
  145.         ConvertWaveFormatToString( &wfxInput, strInputFormat );   
  146.  
  147.         SetWindowText( hInputFormatText, strInputFormat );
  148.  
  149.         g_bRecording = FALSE;
  150.         ShowWindow( hDlg, SW_SHOW ); 
  151.     }
  152.  
  153.     bDone = FALSE;
  154.     while( !bDone ) 
  155.     { 
  156.         dwResult = MsgWaitForMultipleObjects( 1, &g_hNotificationEvent, 
  157.                                               FALSE, INFINITE, QS_ALLEVENTS );
  158.         switch( dwResult )
  159.         {
  160.             case WAIT_OBJECT_0 + 0:
  161.                 // g_hNotificationEvents[0] is signaled
  162.  
  163.                 // This means that DirectSound just finished playing 
  164.                 // a piece of the buffer, so we need to fill the circular 
  165.                 // buffer with new sound from the wav file
  166.  
  167.                 if( FAILED( hr = RecordCapturedData() ) )
  168.                 {
  169.                     DXTRACE_ERR( TEXT("RecordCapturedData"), hr );
  170.                     MessageBox( hDlg, "Error handling DirectSound notifications. "
  171.                                "Sample will now exit.", "DirectSound Sample", 
  172.                                MB_OK | MB_ICONERROR );
  173.                     bDone = TRUE;
  174.                 }
  175.                 break;
  176.  
  177.             case WAIT_OBJECT_0 + 1:
  178.                 // Windows messages are available
  179.                 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  180.                 { 
  181.                     if( !IsDialogMessage( hDlg, &msg ) )  
  182.                     {
  183.                         TranslateMessage( &msg ); 
  184.                         DispatchMessage( &msg ); 
  185.                     }
  186.  
  187.                     if( msg.message == WM_QUIT )
  188.                         bDone = TRUE;
  189.                 }
  190.                 break;
  191.         }
  192.     }
  193.  
  194.     // Stop the capture and read any data that was not caught by a notification
  195.     StartOrStopRecord( FALSE );
  196.  
  197.     // Clean up everything
  198.     FreeDirectSound();
  199.  
  200.     CloseHandle( g_hNotificationEvent );
  201.  
  202.     return TRUE;
  203. }
  204.  
  205.  
  206.  
  207.  
  208. //-----------------------------------------------------------------------------
  209. // Name: InitDirectSound()
  210. // Desc: Initilizes DirectSound
  211. //-----------------------------------------------------------------------------
  212. HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid )
  213. {
  214.     HRESULT hr;
  215.  
  216.     ZeroMemory( &g_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * 
  217.                                (NUM_REC_NOTIFICATIONS + 1) );
  218.     g_dwCaptureBufferSize = 0;
  219.     g_dwNotifySize        = 0;
  220.     g_pWaveFile           = NULL;
  221.  
  222.     // Initialize COM
  223.     if( FAILED( hr = CoInitialize(NULL) ) )
  224.         return DXTRACE_ERR( TEXT("CoInitialize"), hr );
  225.  
  226.  
  227.     // Create IDirectSoundCapture using the preferred capture device
  228.     if( FAILED( hr = DirectSoundCaptureCreate( pDeviceGuid, &g_pDSCapture, NULL ) ) )
  229.         return DXTRACE_ERR( TEXT("DirectSoundCaptureCreate"), hr );
  230.  
  231.     return S_OK;
  232. }
  233.  
  234.  
  235.  
  236.  
  237. //-----------------------------------------------------------------------------
  238. // Name: FreeDirectSound()
  239. // Desc: Releases DirectSound 
  240. //-----------------------------------------------------------------------------
  241. HRESULT FreeDirectSound()
  242. {
  243.     SAFE_DELETE( g_pWaveFile );
  244.  
  245.     // Release DirectSound interfaces
  246.     SAFE_RELEASE( g_pDSNotify );
  247.     SAFE_RELEASE( g_pDSBCapture );
  248.     SAFE_RELEASE( g_pDSCapture ); 
  249.  
  250.     // Release COM
  251.     CoUninitialize();
  252.  
  253.     return S_OK;
  254. }
  255.  
  256.  
  257.  
  258.  
  259. //-----------------------------------------------------------------------------
  260. // Name: DevicesDlgProc()
  261. // Desc: Handles dialog messages for devices dlg box
  262. //-----------------------------------------------------------------------------
  263. INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  264. {
  265.     HRESULT hr;
  266.  
  267.     switch( msg ) 
  268.     {
  269.     case WM_INITDIALOG:
  270.         if( FAILED( hr = OnInitDevicesDialog( hDlg ) ) )
  271.         {
  272.             DXTRACE_ERR( TEXT("OnInitDevicesDialog"), hr );
  273.             MessageBox( hDlg, "Error scanning DirectSoundCapture devices. "
  274.                         "Sample will now exit.", "DirectSound Sample", 
  275.                         MB_OK | MB_ICONERROR );
  276.             EndDialog( hDlg, IDABORT );
  277.         }
  278.         break;
  279.  
  280.     case WM_COMMAND:
  281.         switch( LOWORD(wParam) )
  282.         {
  283.             case IDCANCEL:
  284.                 EndDialog( hDlg, IDCANCEL );
  285.                 break;
  286.  
  287.             case IDOK:
  288.             {
  289.                 HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO );
  290.  
  291.                 // Get the index of the currently selected devices
  292.                 INT nCaptureIndex = (INT)SendMessage( hCaptureDeviceCombo, CB_GETCURSEL, 0, 0 ); 
  293.  
  294.                 // Get the GUID attached to the combo box item
  295.                 GUID* pCaptureGUID = (GUID*) SendMessage( hCaptureDeviceCombo, CB_GETITEMDATA, 
  296.                                                           nCaptureIndex, 0 );
  297.  
  298.                 // Remember that guid
  299.                 if( pCaptureGUID ) 
  300.                     g_guidCaptureDevice = *pCaptureGUID;
  301.  
  302.                 EndDialog( hDlg, IDOK );
  303.                 break;
  304.             }
  305.  
  306.             default:
  307.                 return FALSE; // Didn't handle message
  308.         }   
  309.         break;
  310.  
  311.     default:
  312.         return FALSE; // Didn't handle message
  313.     }
  314.  
  315.     return TRUE; // Handled message
  316. }
  317.  
  318.  
  319.  
  320.  
  321. //-----------------------------------------------------------------------------
  322. // Name: OnInitDevicesDialog()
  323. // Desc: Initializes the devices dialog
  324. //-----------------------------------------------------------------------------
  325. HRESULT OnInitDevicesDialog( HWND hDlg )
  326. {
  327.     // Enumerate the capture devices and place them in the combo box
  328.     HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO );
  329.     DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)DSoundEnumCallback,
  330.                                  (VOID*)hCaptureDeviceCombo );
  331.  
  332.     // Select the first device in the combo box
  333.     SendMessage( hCaptureDeviceCombo, CB_SETCURSEL, 0, 0 );
  334.  
  335.     return S_OK;
  336. }
  337.  
  338.  
  339.  
  340.  
  341. //-----------------------------------------------------------------------------
  342. // Name: FormatsDlgProc()
  343. // Desc: Handles dialog messages for formats dlg box
  344. //-----------------------------------------------------------------------------
  345. INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  346. {
  347.     HRESULT hr;
  348.  
  349.     switch( msg ) 
  350.     {
  351.     case WM_INITDIALOG:
  352.         if( FAILED( hr = OnInitFormatsDialog( hDlg ) ) )
  353.         {
  354.             DXTRACE_ERR( TEXT("OnInitFormatsDialog"), hr );
  355.             MessageBox( hDlg, "Error scanning DirectSound formats. "
  356.                         "Sample will now exit.", "DirectSound Sample", 
  357.                         MB_OK | MB_ICONERROR );
  358.             EndDialog( hDlg, IDABORT );
  359.         }
  360.         break;
  361.  
  362.     case WM_COMMAND:
  363.         switch( LOWORD(wParam) )
  364.         {
  365.             case IDCANCEL:
  366.                 EndDialog( hDlg, IDCANCEL );
  367.                 break;
  368.  
  369.             case IDOK:
  370.                 if( FAILED( hr = OnFormatsOK( hDlg ) ) )
  371.                 {
  372.                     DXTRACE_ERR( TEXT("OnFormatsOK"), hr );
  373.                     MessageBox( hDlg, "Error accepting DirectSound formats. "
  374.                                 "Sample will now exit.", "DirectSound Sample", 
  375.                                 MB_OK | MB_ICONERROR );
  376.                     EndDialog( hDlg, IDABORT );
  377.                 }
  378.  
  379.                 break;
  380.  
  381.             case IDC_FORMATS_INPUT_LISTBOX:
  382.                 OnInputFormatBoxSelected( hDlg );
  383.                 break;
  384.             
  385.             default:
  386.                 return FALSE; // Didn't handle message
  387.         }   
  388.         break;
  389.  
  390.     default:
  391.         return FALSE; // Didn't handle message
  392.     }
  393.  
  394.     return TRUE; // Handled message
  395. }
  396.  
  397.  
  398.  
  399.  
  400. //-----------------------------------------------------------------------------
  401. // Name: OnInitFormatsDialog()
  402. // Desc: Initializes the formats dialog
  403. //-----------------------------------------------------------------------------
  404. HRESULT OnInitFormatsDialog( HWND hDlg )
  405. {
  406.     HRESULT hr;
  407.  
  408.     if( FAILED( hr = ScanAvailableInputFormats() ) )
  409.         return DXTRACE_ERR( TEXT("ScanAvailableInputFormats"), hr );
  410.  
  411.     HWND hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  412.     FillFormatListBox( hInputList, g_abInputFormatSupported );
  413.  
  414.     return S_OK;
  415. }
  416.  
  417.  
  418.  
  419.  
  420. //-----------------------------------------------------------------------------
  421. // Name: ScanAvailableInputFormats()
  422. // Desc: Tests to see if 16 different standard wave formats are supported by
  423. //       the capture device 
  424. //-----------------------------------------------------------------------------
  425. HRESULT ScanAvailableInputFormats()
  426. {
  427.     HRESULT       hr;
  428.     WAVEFORMATEX  wfx;
  429.     HCURSOR       hCursor;
  430.     DSCBUFFERDESC dscbd;
  431.     LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer = NULL;
  432.     
  433.     // This might take a second or two, so throw up the hourglass
  434.     hCursor = GetCursor();
  435.     SetCursor( LoadCursor( NULL, IDC_WAIT ) );
  436.     
  437.     ZeroMemory( &wfx, sizeof(wfx));
  438.     wfx.wFormatTag = WAVE_FORMAT_PCM;
  439.  
  440.     ZeroMemory( &dscbd, sizeof(dscbd) );
  441.     dscbd.dwSize = sizeof(dscbd);
  442.  
  443.     // Try 16 different standard formats to see if they are supported
  444.     for( INT iIndex = 0; iIndex < 16; iIndex++ )
  445.     {
  446.         GetWaveFormatFromIndex( iIndex, &wfx );
  447.  
  448.         // To test if a capture format is supported, try to create a 
  449.         // new capture buffer using a specific format.  If it works
  450.         // then the format is supported, otherwise not.
  451.         dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
  452.         dscbd.lpwfxFormat = &wfx;
  453.         
  454.         if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, 
  455.                                                             &pDSCaptureBuffer, 
  456.                                                             NULL ) ) )
  457.             g_abInputFormatSupported[ iIndex ] = FALSE;
  458.         else
  459.             g_abInputFormatSupported[ iIndex ] = TRUE;
  460.  
  461.         SAFE_RELEASE( pDSCaptureBuffer );
  462.     }
  463.  
  464.     SetCursor( hCursor );
  465.  
  466.     return S_OK;
  467. }
  468.  
  469.  
  470.  
  471.  
  472. //-----------------------------------------------------------------------------
  473. // Name: GetWaveFormatFromIndex()
  474. // Desc: Returns 16 different wave formats based on nIndex
  475. //-----------------------------------------------------------------------------
  476. VOID GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx )
  477. {
  478.     INT iSampleRate = nIndex / 4;
  479.     INT iType = nIndex % 4;
  480.  
  481.     switch( iSampleRate )
  482.     {
  483.         case 0: pwfx->nSamplesPerSec =  8000; break;
  484.         case 1: pwfx->nSamplesPerSec = 11025; break;
  485.         case 2: pwfx->nSamplesPerSec = 22050; break;
  486.         case 3: pwfx->nSamplesPerSec = 44100; break;
  487.     }
  488.  
  489.     switch( iType )
  490.     {
  491.         case 0: pwfx->wBitsPerSample =  8; pwfx->nChannels = 1; break;
  492.         case 1: pwfx->wBitsPerSample = 16; pwfx->nChannels = 1; break;
  493.         case 2: pwfx->wBitsPerSample =  8; pwfx->nChannels = 2; break;
  494.         case 3: pwfx->wBitsPerSample = 16; pwfx->nChannels = 2; break;
  495.     }
  496.  
  497.     pwfx->nBlockAlign = pwfx->nChannels * ( pwfx->wBitsPerSample / 8 );
  498.     pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
  499. }
  500.  
  501.  
  502.  
  503.  
  504. //-----------------------------------------------------------------------------
  505. // Name: FillFormatListBox()
  506. // Desc: Fills the format list box based on the availible formats
  507. //-----------------------------------------------------------------------------
  508. HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported )
  509. {
  510.     TCHAR        strFormatName[255];
  511.     WAVEFORMATEX wfx;
  512.     DWORD        dwStringIndex;
  513.  
  514.     SendMessage( hListBox, LB_RESETCONTENT, 0, 0 );
  515.  
  516.     for( INT iIndex = 0; iIndex < 16; iIndex++ )
  517.     {
  518.         if( aFormatSupported[ iIndex ] )
  519.         {
  520.             // Turn the index into a WAVEFORMATEX then turn that into a
  521.             // string and put the string in the listbox
  522.             GetWaveFormatFromIndex( iIndex, &wfx );
  523.             ConvertWaveFormatToString( &wfx, strFormatName );
  524.             dwStringIndex = (DWORD)SendMessage( hListBox, LB_ADDSTRING, 0, 
  525.                                                 (LPARAM) (LPCTSTR) strFormatName );
  526.             SendMessage( hListBox, LB_SETITEMDATA, dwStringIndex, iIndex );
  527.         }
  528.     }
  529.  
  530.     return S_OK;
  531. }
  532.  
  533.  
  534.  
  535.  
  536. //-----------------------------------------------------------------------------
  537. // Name: ConvertWaveFormatToString()
  538. // Desc: Converts a wave format to a text string
  539. //-----------------------------------------------------------------------------
  540. VOID ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName )
  541. {
  542.     wsprintf( strFormatName, 
  543.               TEXT("%u Hz, %u-bit %s"), 
  544.               pwfx->nSamplesPerSec, 
  545.               pwfx->wBitsPerSample, 
  546.               ( pwfx->nChannels == 1 ) ? TEXT("Mono") : TEXT("Stereo") );
  547. }
  548.  
  549.  
  550.  
  551.  
  552. //-----------------------------------------------------------------------------
  553. // Name: OnInputFormatBoxSelected()
  554. // Desc: Enables the OK button when there is a selection
  555. //-----------------------------------------------------------------------------
  556. HRESULT OnInputFormatBoxSelected( HWND hDlg )
  557. {
  558.     HWND hInputList  = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  559.     HWND hOK         = GetDlgItem( hDlg, IDOK );
  560.  
  561.     if( SendMessage( hInputList,  LB_GETCURSEL, 0, 0 ) != -1 )        
  562.         EnableWindow( hOK, TRUE );
  563.     else
  564.         EnableWindow( hOK, FALSE );
  565.  
  566.     return S_OK;
  567. }
  568.  
  569.  
  570.  
  571.  
  572. //-----------------------------------------------------------------------------
  573. // Name: OnFormatsOK()
  574. // Desc: Creates a capture buffer format based on what was selected
  575. //-----------------------------------------------------------------------------
  576. HRESULT OnFormatsOK( HWND hDlg )
  577. {
  578.     HRESULT       hr;
  579.     DWORD         dwInputSelect;
  580.     DWORD         dwInputWavIndex;
  581.     HWND          hInputList;
  582.  
  583.     hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  584.  
  585.     dwInputSelect   = (DWORD)SendMessage( hInputList, LB_GETCURSEL, 0, 0 );
  586.     dwInputWavIndex = (DWORD)SendMessage( hInputList, LB_GETITEMDATA, dwInputSelect, 0 );
  587.  
  588.     ZeroMemory( &g_wfxInput, sizeof(g_wfxInput));
  589.     g_wfxInput.wFormatTag = WAVE_FORMAT_PCM;
  590.  
  591.     GetWaveFormatFromIndex( dwInputWavIndex, &g_wfxInput );
  592.  
  593.     if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) )
  594.         return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr );
  595.  
  596.     EndDialog( hDlg, IDOK );
  597.  
  598.     return S_OK;
  599. }
  600.  
  601.  
  602.  
  603.  
  604. //-----------------------------------------------------------------------------
  605. // Name: MainDlgProc()
  606. // Desc: Handles dialog messages
  607. //-----------------------------------------------------------------------------
  608. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  609. {
  610.     HRESULT hr;
  611.  
  612.     switch( msg ) 
  613.     {
  614.     case WM_INITDIALOG:
  615.         OnInitMainDialog( hDlg );
  616.         break;
  617.  
  618.     case WM_COMMAND:
  619.         switch( LOWORD(wParam) )
  620.         {
  621.             case IDCANCEL:
  622.                 PostQuitMessage( 0 );
  623.                 EndDialog( hDlg, IDCANCEL );
  624.                 break; 
  625.  
  626.             case IDC_SOUNDFILE:
  627.                 OnSaveSoundFile( hDlg );
  628.                 break;
  629.  
  630.             case IDC_RECORD:
  631.                 g_bRecording = !g_bRecording;
  632.                 if( FAILED( hr = StartOrStopRecord( g_bRecording ) ) )
  633.                 {
  634.                     DXTRACE_ERR( TEXT("StartOrStopRecord"), hr );
  635.                     MessageBox( hDlg, "Error with DirectSoundCapture buffer."                            
  636.                                 "Sample will now exit.", "DirectSound Sample", 
  637.                                 MB_OK | MB_ICONERROR );
  638.                     PostQuitMessage( 0 );
  639.                     EndDialog( hDlg, IDABORT );
  640.                 }
  641.  
  642.                 if( !g_bRecording )
  643.                     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE );
  644.  
  645.                 break;
  646.  
  647.             default:
  648.                 return FALSE; // Didn't handle message
  649.         }
  650.         break;
  651.  
  652.     default:
  653.         return FALSE; // Didn't handle message
  654.     }
  655.  
  656.     return TRUE; // Handled message
  657. }
  658.  
  659.  
  660.  
  661.  
  662. //-----------------------------------------------------------------------------
  663. // Name: OnInitMainDialog()
  664. // Desc: Initializes the main dialog
  665. //-----------------------------------------------------------------------------
  666. HRESULT OnInitMainDialog( HWND hDlg )
  667. {
  668.     // Load the icon
  669.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDR_MAINFRAME ) );
  670.  
  671.     // Set the icon for this dialog.
  672.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  673.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  674.  
  675.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE);
  676.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("No file loaded.") );
  677.  
  678.     return S_OK;
  679. }
  680.  
  681.  
  682.  
  683.  
  684. //-----------------------------------------------------------------------------
  685. // Name: DSoundEnumCallback()
  686. // Desc: Enumeration callback called by DirectSoundEnumerate
  687. //-----------------------------------------------------------------------------
  688. INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName,
  689.                                   VOID* pContext )
  690. {
  691.     // Set aside static storage space for 20 audio drivers
  692.     static GUID  AudioDriverGUIDs[20];
  693.     static DWORD dwAudioDriverIndex = 0;
  694.  
  695.     GUID* pTemp  = NULL;
  696.  
  697.     if( pGUID )
  698.     {
  699.         if( dwAudioDriverIndex >= 20 )
  700.             return TRUE;
  701.  
  702.         pTemp = &AudioDriverGUIDs[dwAudioDriverIndex++];
  703.         memcpy( pTemp, pGUID, sizeof(GUID) );
  704.     }
  705.  
  706.     HWND hSoundDeviceCombo = (HWND)pContext;
  707.  
  708.     // Add the string to the combo box
  709.     SendMessage( hSoundDeviceCombo, CB_ADDSTRING, 
  710.                  0, (LPARAM) (LPCTSTR) strDesc );
  711.  
  712.     // Get the index of the string in the combo box
  713.     INT nIndex = (INT)SendMessage( hSoundDeviceCombo, CB_FINDSTRING, 
  714.                                    0, (LPARAM) (LPCTSTR) strDesc );
  715.  
  716.     // Set the item data to a pointer to the static guid stored in AudioDriverGUIDs
  717.     SendMessage( hSoundDeviceCombo, CB_SETITEMDATA, 
  718.                  nIndex, (LPARAM) pTemp );
  719.  
  720.     return TRUE;
  721. }
  722.  
  723.  
  724.  
  725.  
  726. //-----------------------------------------------------------------------------
  727. // Name: OnSaveSoundFile()
  728. // Desc: Called when the user requests to save to a sound file
  729. //-----------------------------------------------------------------------------
  730. VOID OnSaveSoundFile( HWND hDlg ) 
  731. {
  732.     HRESULT hr;
  733.  
  734.     static TCHAR strFileName[MAX_PATH] = TEXT("");
  735.     static TCHAR strPath[MAX_PATH] = TEXT("");
  736.  
  737.     // Setup the OPENFILENAME structure
  738.     OPENFILENAME ofn = { sizeof(OPENFILENAME), hDlg, NULL,
  739.                          TEXT("Wave Files\0*.wav\0All Files\0*.*\0\0"), NULL,
  740.                          0, 1, strFileName, MAX_PATH, NULL, 0, strPath,
  741.                          TEXT("Save Sound File"),
  742.                          OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | 
  743.                          OFN_HIDEREADONLY    | OFN_NOREADONLYRETURN, 
  744.                          0, 0, TEXT(".wav"), 0, NULL, NULL };
  745.  
  746.     // Get the default media path (something like C:\WINDOWS\MEDIA)
  747.     if( '\0' == strPath[0] )
  748.     {
  749.         GetWindowsDirectory( strPath, MAX_PATH );
  750.         if( strcmp( &strPath[strlen(strPath)], TEXT("\\") ) )
  751.             strcat( strPath, TEXT("\\") );
  752.         strcat( strPath, TEXT("MEDIA") );
  753.     }
  754.  
  755.     if( g_bRecording )
  756.     {
  757.         // Stop the capture and read any data that 
  758.         // was not caught by a notification
  759.         StartOrStopRecord( FALSE );
  760.         g_bRecording = FALSE;
  761.     }
  762.  
  763.     // Update the UI controls to show the sound as loading a file
  764.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE );
  765.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Saving file...") );
  766.  
  767.     // Display the SaveFileName dialog. Then, try to load the specified file
  768.     if( TRUE != GetSaveFileName( &ofn ) )
  769.     {
  770.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Save aborted.") );
  771.         return;
  772.     }
  773.  
  774.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("") );
  775.  
  776.     SAFE_DELETE( g_pWaveFile );
  777.     g_pWaveFile = new CWaveFile;
  778.  
  779.     // Get the format of the capture buffer in g_wfxCaptureWaveFormat
  780.     WAVEFORMATEX wfxCaptureWaveFormat;
  781.     ZeroMemory( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX) );
  782.     g_pDSBCapture->GetFormat( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX), NULL );
  783.  
  784.     // Load the wave file
  785.     if( FAILED( hr = g_pWaveFile->Open( strFileName, &wfxCaptureWaveFormat, WAVEFILE_WRITE ) ) )
  786.     {
  787.         DXTRACE_ERR( TEXT("Open"), hr );
  788.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Can not create wave file.") );
  789.         return;
  790.     }
  791.  
  792.     // Update the UI controls to show the sound as the file is loaded
  793.     SetDlgItemText( hDlg, IDC_FILENAME, strFileName );
  794.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), TRUE );
  795.  
  796.     // Remember the path for next time
  797.     strcpy( strPath, strFileName );
  798.     char* strLastSlash = strrchr( strPath, '\\' );
  799.     strLastSlash[0] = '\0';
  800. }
  801.  
  802.  
  803.  
  804.  
  805. //-----------------------------------------------------------------------------
  806. // Name: CreateCaptureBuffer()
  807. // Desc: Creates a capture buffer and sets the format 
  808. //-----------------------------------------------------------------------------
  809. HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput )
  810. {
  811.     HRESULT hr;
  812.     DSCBUFFERDESC dscbd;
  813.  
  814.     SAFE_RELEASE( g_pDSNotify );
  815.     SAFE_RELEASE( g_pDSBCapture );
  816.  
  817.     // Set the notification size
  818.     g_dwNotifySize = MAX( 1024, pwfxInput->nAvgBytesPerSec / 8 );
  819.     g_dwNotifySize -= g_dwNotifySize % pwfxInput->nBlockAlign;   
  820.  
  821.     // Set the buffer sizes 
  822.     g_dwCaptureBufferSize = g_dwNotifySize * NUM_REC_NOTIFICATIONS;
  823.  
  824.     SAFE_RELEASE( g_pDSNotify );
  825.     SAFE_RELEASE( g_pDSBCapture );
  826.  
  827.     // Create the capture buffer
  828.     ZeroMemory( &dscbd, sizeof(dscbd) );
  829.     dscbd.dwSize        = sizeof(dscbd);
  830.     dscbd.dwBufferBytes = g_dwCaptureBufferSize;
  831.     dscbd.lpwfxFormat   = pwfxInput; // Set the format during creatation
  832.  
  833.     if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, 
  834.                                                         &g_pDSBCapture, 
  835.                                                         NULL ) ) )
  836.         return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr );
  837.  
  838.     g_dwNextCaptureOffset = 0;
  839.  
  840.     if( FAILED( hr = InitNotifications() ) )
  841.         return DXTRACE_ERR( TEXT("InitNotifications"), hr );
  842.  
  843.     return S_OK;
  844. }
  845.  
  846.  
  847.  
  848.  
  849. //-----------------------------------------------------------------------------
  850. // Name: InitNotifications()
  851. // Desc: Inits the notifications on the capture buffer which are handled
  852. //       in WinMain()
  853. //-----------------------------------------------------------------------------
  854. HRESULT InitNotifications()
  855. {
  856.     HRESULT hr; 
  857.  
  858.     if( NULL == g_pDSBCapture )
  859.         return E_FAIL;
  860.  
  861.     // Create a notification event, for when the sound stops playing
  862.     if( FAILED( hr = g_pDSBCapture->QueryInterface( IID_IDirectSoundNotify, 
  863.                                                     (VOID**)&g_pDSNotify ) ) )
  864.         return DXTRACE_ERR( TEXT("QueryInterface"), hr );
  865.  
  866.     // Setup the notification positions
  867.     for( INT i = 0; i < NUM_REC_NOTIFICATIONS; i++ )
  868.     {
  869.         g_aPosNotify[i].dwOffset = (g_dwNotifySize * i) + g_dwNotifySize - 1;
  870.         g_aPosNotify[i].hEventNotify = g_hNotificationEvent;             
  871.     }
  872.     
  873.     // Tell DirectSound when to notify us. the notification will come in the from 
  874.     // of signaled events that are handled in WinMain()
  875.     if( FAILED( hr = g_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, 
  876.                                                             g_aPosNotify ) ) )
  877.         return DXTRACE_ERR( TEXT("SetNotificationPositions"), hr );
  878.  
  879.     return S_OK;
  880. }
  881.  
  882.  
  883.  
  884.  
  885. //-----------------------------------------------------------------------------
  886. // Name: StartOrStopRecord()
  887. // Desc: Starts or stops the capture buffer from recording
  888. //-----------------------------------------------------------------------------
  889. HRESULT StartOrStopRecord( BOOL bStartRecording )
  890. {
  891.     HRESULT hr;
  892.  
  893.     if( bStartRecording )
  894.     {
  895.         // Create a capture buffer, and tell the capture 
  896.         // buffer to start recording   
  897.         if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) )
  898.             return DXTRACE_ERR( TEXT("CreateCaptureBuffer"), hr );
  899.  
  900.         if( FAILED( hr = g_pDSBCapture->Start( DSCBSTART_LOOPING ) ) )
  901.             return DXTRACE_ERR( TEXT("Start"), hr );
  902.     }
  903.     else
  904.     {
  905.         // Stop the capture and read any data that 
  906.         // was not caught by a notification
  907.         if( NULL == g_pDSBCapture )
  908.             return S_OK;
  909.  
  910.         // Stop the buffer, and read any data that was not 
  911.         // caught by a notification
  912.         if( FAILED( hr = g_pDSBCapture->Stop() ) )
  913.             return DXTRACE_ERR( TEXT("Stop"), hr );
  914.  
  915.         if( FAILED( hr = RecordCapturedData() ) )
  916.             return DXTRACE_ERR( TEXT("RecordCapturedData"), hr );
  917.  
  918.         // Close the wav file
  919.         SAFE_DELETE( g_pWaveFile );
  920.     }
  921.  
  922.     return S_OK;
  923. }
  924.  
  925.  
  926.  
  927.  
  928. //-----------------------------------------------------------------------------
  929. // Name: RecordCapturedData()
  930. // Desc: Copies data from the capture buffer to the output buffer 
  931. //-----------------------------------------------------------------------------
  932. HRESULT RecordCapturedData() 
  933. {
  934.     HRESULT hr;
  935.     VOID*   pbCaptureData    = NULL;
  936.     DWORD   dwCaptureLength;
  937.     VOID*   pbCaptureData2   = NULL;
  938.     DWORD   dwCaptureLength2;
  939.     VOID*   pbPlayData       = NULL;
  940.     UINT    dwDataWrote;
  941.     DWORD   dwReadPos;
  942.     DWORD   dwCapturePos;
  943.     LONG lLockSize;
  944.  
  945.     if( NULL == g_pDSBCapture )
  946.         return S_FALSE;
  947.     if( NULL == g_pWaveFile )
  948.         return S_FALSE;
  949.  
  950.     if( FAILED( hr = g_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )
  951.         return DXTRACE_ERR( TEXT("GetCurrentPosition"), hr );
  952.  
  953.     lLockSize = dwReadPos - g_dwNextCaptureOffset;
  954.     if( lLockSize < 0 )
  955.         lLockSize += g_dwCaptureBufferSize;
  956.  
  957.     // Block align lock size so that we are always write on a boundary
  958.     lLockSize -= (lLockSize % g_dwNotifySize);
  959.  
  960.     if( lLockSize == 0 )
  961.         return S_FALSE;
  962.  
  963.     // Lock the capture buffer down
  964.     if( FAILED( hr = g_pDSBCapture->Lock( g_dwNextCaptureOffset, lLockSize, 
  965.                                           &pbCaptureData, &dwCaptureLength, 
  966.                                           &pbCaptureData2, &dwCaptureLength2, 0L ) ) )
  967.         return DXTRACE_ERR( TEXT("Lock"), hr );
  968.  
  969.     // Write the data into the wav file
  970.     if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength, 
  971.                                               (BYTE*)pbCaptureData, 
  972.                                               &dwDataWrote ) ) )
  973.         return DXTRACE_ERR( TEXT("Write"), hr );
  974.  
  975.     // Move the capture offset along
  976.     g_dwNextCaptureOffset += dwCaptureLength; 
  977.     g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer
  978.  
  979.     if( pbCaptureData2 != NULL )
  980.     {
  981.         // Write the data into the wav file
  982.         if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength2, 
  983.                                                   (BYTE*)pbCaptureData2, 
  984.                                                   &dwDataWrote ) ) )
  985.             return DXTRACE_ERR( TEXT("Write"), hr );
  986.  
  987.         // Move the capture offset along
  988.         g_dwNextCaptureOffset += dwCaptureLength2; 
  989.         g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer
  990.     }
  991.  
  992.     // Unlock the capture buffer
  993.     g_pDSBCapture->Unlock( pbCaptureData,  dwCaptureLength, 
  994.                            pbCaptureData2, dwCaptureLength2 );
  995.  
  996.  
  997.     return S_OK;
  998. }
  999.  
  1000.  
  1001.  
  1002.